﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Task4_Matrices
{
    class Task4_Matrices
    {
        enum Directions
        {
            up = 8,
            left = 6,
            down = 2,
            right = 4
        };

        static void Main(string[] args)
        {
            int matrixSize;
            GetMatrixSizeFromInput(out matrixSize);


            int[,] matrixA = GenerateMatrixA(matrixSize);
            Console.WriteLine("Example a)");
            PrintMatrix(matrixA, matrixSize);

            Console.WriteLine();

            int[,] matrixB = GenerateMatrixB(matrixSize);
            Console.WriteLine("Example b)");
            PrintMatrix(matrixB, matrixSize);

            Console.WriteLine();

            int[,] matrixC = GenerateMatrixC(matrixSize);
            Console.WriteLine("Example c)");
            PrintMatrix(matrixC, matrixSize);

            Console.WriteLine();

            int[,] matrixD = GenerateMatrixD(matrixSize);
            Console.WriteLine("Example d)");
            PrintMatrix(matrixD, matrixSize);
        }

        private static void GetMatrixSizeFromInput(out int matrixSize)
        {
            Console.Write("Please, enter the size of the matrix: ");
            string inputMatrixSizeLine = Console.ReadLine();
            bool parseStringResult = int.TryParse(inputMatrixSizeLine, out matrixSize);
            while (!parseStringResult)
            {
                Console.Write("Incorrect input! Please, enter the size of the matrix: ");
                inputMatrixSizeLine = Console.ReadLine();
                parseStringResult = int.TryParse(inputMatrixSizeLine, out matrixSize);
            }
        }

        private static int[,] GenerateMatrixA(int matrixSize)
        {
            if (matrixSize == null)
            {
                throw new NullReferenceException();
            }
            if (matrixSize < 1)
            {
                throw new ArgumentOutOfRangeException();
            }

            int[,] matrix = new int[matrixSize, matrixSize];
            int currentNumber = 1;
            for (int currentDiagonalPosition = matrixSize - 1; currentDiagonalPosition >= 0 ; currentDiagonalPosition--)
            {
                for (int i = currentDiagonalPosition, j = matrixSize - 1; i < matrixSize && j < matrixSize; i++, j--)
                {
                    matrix[j, i] = currentNumber;
                    currentNumber++;
                }
            }

            for (int currentDiagonalPosition = matrixSize - 2; currentDiagonalPosition >= 0; currentDiagonalPosition--)
            {
                for (int i = currentDiagonalPosition, j = 0; i >= 0 && j < matrixSize; i--, j++)
                {
                    matrix[i, j] = currentNumber;
                    currentNumber++;
                }
            }

            return matrix;
        }

        private static int[,] GenerateMatrixB(int matrixSize)
        {
            if (matrixSize == null)
            {
                throw new NullReferenceException();
            }
            if (matrixSize < 1)
            {
                throw new ArgumentOutOfRangeException();
            }

            int[,] matrix = new int[matrixSize, matrixSize];
            int currentNumber = 1;
            for (int currentDiagonalPosition = matrixSize - 1; currentDiagonalPosition >= 0; currentDiagonalPosition--)
            {
                for (int i = currentDiagonalPosition, j = 0; i < matrixSize && j < matrixSize; i++, j++)
                {
                    matrix[i, j] = currentNumber;
                    currentNumber++;
                }
            }

            for (int currentDiagonalPosition = 1; currentDiagonalPosition < matrixSize; currentDiagonalPosition++)
            {
                for (int i = currentDiagonalPosition, j = 0; i < matrixSize && j < matrixSize - 1; i++, j++)
                {
                    matrix[j, i] = currentNumber;
                    currentNumber++;
                }
            }

            return matrix;
        }

        private static int[,] GenerateMatrixC(int matrixSize)
        {
            if (matrixSize == null)
            {
                throw new NullReferenceException();
            }
            if (matrixSize < 1)
            {
                throw new ArgumentOutOfRangeException();
            }

            int[,] matrix = new int[matrixSize, matrixSize];
            int currentNumber = 1;

            int row = 0;
            int column = 0;
            Directions direction = Directions.down;
            
            int topBorder = 0;
            int rightBorder = matrixSize - 1;
            int bottomBorder = matrixSize - 1;
            int leftBorder = 1;

            while (IsInsideMatrix(row, column, matrixSize) && matrix[row, column] == 0)
            {
                matrix[row, column] = currentNumber;
                if (direction == Directions.down)
                {
                    if (row < bottomBorder)
                    {
                        row++;
                    }
                    else
                    {
                        direction = Directions.right;
                        column++;
                        bottomBorder--;
                    }
                }
                else if (direction == Directions.right)
                {
                    if (column < rightBorder)
                    {
                        column++;
                    }
                    else
                    {
                        direction = Directions.up;
                        row--;
                        rightBorder--;
                    }
                }
                else if (direction == Directions.up)
                {
                    if (row > topBorder)
                    {
                        row--;
                    }
                    else
                    {
                        direction = Directions.left;
                        column--;
                        topBorder++;
                    }
                }
                else if (direction == Directions.left)
                {
                    if (column > leftBorder)
                    {
                        column--;
                    }
                    else
                    {
                        direction = Directions.down;
                        row++;
                        leftBorder++;
                    }
                }
                currentNumber++;
            }

            return matrix;
        }

        private static int[,] GenerateMatrixD(int matrixSize)
        {
            if (matrixSize == null)
            {
                throw new NullReferenceException();
            }
            if (matrixSize < 1)
            {
                throw new ArgumentOutOfRangeException();
            }

            int[,] matrix = new int[matrixSize, matrixSize];
            int currentNumber = 1;

            int column;
            if (matrixSize % 2 == 1)
            {
                column = matrixSize / 2;
            }
            else
            {
                column = (matrixSize / 2) - 1; 
            }
            int row = matrixSize / 2;
            Directions direction = Directions.up;

            int topBorder = row - 1;
            int rightBorder = column + 1;
            int bottomBorder = row + 1;
            int leftBorder = column - 1;

            bool isInsideMartix = IsInsideMatrix(row, column, matrixSize);
            bool isEmptyElement = IsEmptyElement(matrix, matrixSize, row, column);

            while (isInsideMartix && isEmptyElement)
            {
                matrix[row, column] = currentNumber;
                if (direction == Directions.up)
                {
                    if (row > topBorder)
                    {
                        row--;
                    }
                    else
                    {
                        direction = Directions.right;
                        column++;
                        topBorder--;
                    }
                }
                else if (direction == Directions.right)
                {
                    if (column < rightBorder)
                    {
                        column++;
                    }
                    else
                    {
                        direction = Directions.down;
                        row++;
                        rightBorder++;
                    }
                }
                else if (direction == Directions.down)
                {
                    if (row < bottomBorder)
                    {
                        row++;
                    }
                    else
                    {
                        direction = Directions.left;
                        column--;
                        bottomBorder++;
                    }
                }
                else if (direction == Directions.left)
                {
                    if (column > leftBorder)
                    {
                        column--;
                    }
                    else
                    {
                        direction = Directions.up;
                        row--;
                        leftBorder--;
                    }
                }
                isInsideMartix = IsInsideMatrix(row, column, matrixSize);
                isEmptyElement = IsEmptyElement(matrix, matrixSize, row, column);
                currentNumber++;
            }

            return matrix;
        }

        private static bool IsEmptyElement(int[,] matrix, int matrixSize, int row, int column)
        {
            if (matrix == null)
            {
                throw new NullReferenceException();
            }
            if (matrixSize == null)
            {
                throw new NullReferenceException();
            }
            if (matrixSize < 1)
            {
                throw new ArgumentOutOfRangeException();
            }
            if (row == null)
            {
                throw new NullReferenceException();
            }
            if (column == null)
            {
                throw new NullReferenceException();
            }

            bool result;
            if (IsInsideMatrix(row, column, matrixSize))
            {
                result = (matrix[row, column] == 0);
            }
            else
            {
                result = false;
            }
            return result;
        }

        private static bool IsInsideMatrix(int row, int column, int matrixSize)
        {
            if (row == null)
            {
                throw new NullReferenceException();
            } 
            if (column == null)
            {
                throw new NullReferenceException();
            }
            if (matrixSize == null)
            {
                throw new NullReferenceException();
            }
            if (matrixSize < 1)
            {
                throw new ArgumentOutOfRangeException();
            }

            bool result = (0 <= row && row < matrixSize)
                && (0 <= column && column < matrixSize);
            return result;
        }

        private static void PrintMatrix(int[,] matrix, int matrixSize)
        {
            if (matrixSize == null)
            {
                throw new NullReferenceException();
            }
            if (matrixSize < 1)
            {
                throw new ArgumentOutOfRangeException();
            }
            if (matrix == null)
            {
                throw new NullReferenceException();
            }

            for (int i = 0; i < matrixSize; i++)
            {
                for (int j = 0; j < matrixSize; j++)
                {
                    Console.Write("{0, 3} ", matrix[i, j]);
                }
                Console.WriteLine();
            }
        }
    }
}